package com.migu.spms.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.migu.spms.common.JsonTool;
import com.migu.spms.common.StringUtil;
import com.migu.spms.constant.Constant;
import com.migu.spms.dao.entity.ChangeHistoryCriteria;
import com.migu.spms.dao.entity.FlowCriteria;
import com.migu.spms.dao.entity.FlowDO;
import com.migu.spms.dao.entity.FunctionRequireCriteria;
import com.migu.spms.dao.entity.FunctionRequireDO;
import com.migu.spms.dao.entity.ProcessTaskDO;
import com.migu.spms.dao.entity.TaskDO;
import com.migu.spms.dao.inf.IChangeHistoryDO;
import com.migu.spms.dao.inf.IFunctionRequireDO;
import com.migu.spms.dao.inf.IProcessTaskDao;
import com.migu.spms.exception.CommonRuntimeException;
import com.migu.spms.service.data.EmailInfo;
import com.migu.spms.service.data.FlowEvent;
import com.migu.spms.service.data.User;
import com.migu.spms.service.inf.IEmailService;
import com.migu.spms.service.inf.IFunctionFlowService;
import com.migu.spms.utils.ProcessUtil;
import com.migu.spms.utils.PropertiesCopyUtil;
import com.migu.spms.utils.exception.ServiceException;
import com.migu.spms.webapi.data.FunctionFlowRequest;
@Transactional
@Component(value = "functionFlowServiceImpl")
public class FunctionFlowServiceImpl implements IFunctionFlowService {
	private static Logger logger = LoggerFactory.getLogger(FunctionFlowServiceImpl.class);

	@Resource(name = "taskService")
	private TaskService taskService;

	@Resource(name = "runtimeService")
	private RuntimeService runtimeService;

	@Resource(name = "repositoryService")
	private RepositoryService repositoryService;

	@Resource(name = "identityService")
	private IdentityService identityService;

	@Resource(name = "IProcessTaskDao")
	private IProcessTaskDao processTaskDao;

	@Resource(name = "processTaskType")
	private Set<String> processTaskType;

	@Resource(name = "IFunctionRequireDO")
	private IFunctionRequireDO functionRequireDao;
	
	@Resource(name = "IChangeHistoryDO")
	private IChangeHistoryDO changeHistoryDao;
	
	@Resource(name = "emailServiceImpl")
	private IEmailService emailService;

	@Override
	public void operateFlow(FunctionFlowRequest flowRequest, User user) {
		String operate = flowRequest.getOperate();
		if (!operate.equals(Constant.flowOperate.REVERSE)&&!operate.equals(Constant.flowOperate.EDIT)) {
			handProcessFlow(flowRequest, user);
		} else {
			//任务转交直接指定候选人
			taskAssignee(flowRequest, user);
		}
	}

	/**
	 * 启动流程实例
	 * 
	 * @param primaryKey 需求主键ID
	 * @param user 创建人
	 * @param users 开发人员
	 * @param operator 操作
	 */
	@Override
	public void startProcessFlow(long primaryKey, User user) {
		FunctionRequireDO functionRequireDO = functionRequireDao.selectByPrimaryKey(primaryKey);
		// 如果新建时未传入当前处理人既开发负责人员时，不启动流程引擎
		if (functionRequireDO.getDevelopResponsibleId() != null && !StringUtil.isEmpty(functionRequireDO.getDevelopResponsibleId().toString())) {

			ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
					.processDefinitionKey(Constant.FlowValiable.PROCESS_DEFINITION_KEY).latestVersion().singleResult();

			FunctionRequireCriteria functionRequireCriteria = new FunctionRequireCriteria();
			functionRequireCriteria.setId(functionRequireDO.getId());
			Map<String, Object> variables = new HashMap<String, Object>();
			if (StringUtil.isEmpty(functionRequireDO.getProcessInstanceId())) {
				long currentHanderUid = functionRequireDO.getDevelopResponsibleId();
				String currentHanderName = functionRequireDO.getDevelopResponsibleName();
				String assignee = currentHanderUid + "," + currentHanderName;
				// 设置当前处理人的流程变量
				variables.put("code", Constant.flowOperate.DEVELOPING);
				variables.put("uid", assignee);
				// 设置objId 为数据表中的 businessKey 与业务相关联, primaryKey 为需求的主键ID.
				String key = Constant.FlowValiable.PROCESS_DEFINITION_KEY;
				variables.put(Constant.FlowValiable.PROCESS_DEFINITION_ID, processDefinition.getId());
				// 启动流程实例
				ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, variables);
				// 查询下一任务
				TaskDO taskDO = searchNextTask(processInstance.getId(),"");
				functionRequireCriteria.setProcessInstanceId(taskDO.getProcessInstanceId());
				functionRequireCriteria.setFlowInfo(taskDO.getFlowEvents());
				functionRequireCriteria.setStatus(taskDO.getTaskStatus());
				functionRequireCriteria.setTaskId(taskDO.getTaskId());
				if(StringUtil.isEmpty(functionRequireCriteria.getCreatorName())){
					functionRequireCriteria.setCurrentHandlerName(currentHanderName);
					functionRequireCriteria.setCurrentHandlerId(currentHanderUid);
				}
				// 更新数据库
				functionRequireDao.updateByPrimaryKeySelective(functionRequireCriteria);
				//增加邮件服务
				try {
					EmailInfo email=new EmailInfo();
					email.setStatus(functionRequireCriteria.getStatus());
					email.setApplyId(functionRequireCriteria.getId());
					email.setCurrent_user_id(String.valueOf(functionRequireCriteria.getCurrentHandlerId()));
					email.setCurrent_user_name(functionRequireCriteria.getCurrentHandlerName());
					emailService.sendBacklog(email);
				} catch (Exception e) {
					logger.error(e.toString());
				}
			}
		}
	}
	
//    @PostConstruct
	public void deployProcessEnginer() {
		String deploymentId = null;
		try {
			// 创建部署对象
			// 加载流程的配置文件和图片
			Deployment deployment = repositoryService.createDeployment()
					.addClasspathResource("diagrams/function.bpmn")
					.addClasspathResource("diagrams/function.png")
					.name(Constant.FlowValiable.PROCESS_DEFINITION_KEY)
					.category(Constant.FlowValiable.PROCESS_DEFINITION_GROUP)
					.deploy();

			// 获取流程定义key, 和最新的流程id
			deploymentId = deployment.getId();
			ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
					.deploymentId(deploymentId).latestVersion().singleResult();
			if (null == processDefinition) {
				throw new Exception("部署新流程异常, 部署流程后，根据deploymentId获取流程定义为空， deploymentId:" + deploymentId);
			}

			// 注册表processDefinition的所有任务节点
			BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
			Collection<FlowElement> lstFlowElement = ProcessUtil.getProcesAllTaskCanvas(bpmnModel);
			if (null == lstFlowElement || lstFlowElement.isEmpty()) {
				throw new Exception("部署新流程异常, 部署流程后，获取流程定义的任务节点为空， deploymentId:" + deploymentId);
			}

			String processDefinitionId = processDefinition.getId();
			String processDefinitionKey = processDefinition.getKey();
			if (StringUtils.isBlank(processDefinitionKey)) {
				throw new Exception("部署新流程异常, 部署流程后，流程key为空， deploymentId:" + deploymentId);
			}

			List<ProcessTaskDO> lstProcessTaskPo = new ArrayList<ProcessTaskDO>();
			List<FlowDO> lstFlow = new ArrayList<FlowDO>();
			for (FlowElement flowElement : lstFlowElement) {
				String taskType = flowElement.getClass().getSimpleName();
				String taskKey = flowElement.getId();
				String taskName = flowElement.getName();
				String processName = processDefinition.getName();
				Integer proceeVersion = processDefinition.getVersion();
				if (processTaskType.contains(taskType)) {

					ProcessTaskDO processTaskPo = new ProcessTaskDO(processDefinitionId, processDefinitionKey, taskKey,
							taskName, taskType, processName, proceeVersion);
					lstProcessTaskPo.add(processTaskPo);

					List<SequenceFlow> lstInSequenceFlow = ((FlowNode) flowElement).getIncomingFlows();
					for (SequenceFlow sf : lstInSequenceFlow) {
						lstFlow.add(new FlowDO(sf.getId(), sf.getName(), sf.getSourceRef(), sf.getTargetRef(),
								sf.getConditionExpression(), sf.getSkipExpression(), FlowDO.FLOW_IN,
								processDefinitionId, processDefinitionKey, taskKey, taskType, processName,
								proceeVersion, sf.getDocumentation()));
					}

					List<SequenceFlow> lstOutSequenceFlow = ((FlowNode) flowElement).getOutgoingFlows();
					for (SequenceFlow sf : lstOutSequenceFlow) {
						lstFlow.add(new FlowDO(sf.getId(), sf.getName(), sf.getSourceRef(), sf.getTargetRef(),
								sf.getConditionExpression(), sf.getSkipExpression(), FlowDO.FLOW_OUT,
								processDefinitionId, processDefinitionKey, taskKey, taskType, processName,
								proceeVersion, sf.getDocumentation()));
					}
				}
			}

			if (lstProcessTaskPo.isEmpty() || lstFlow.isEmpty()) {
				this.repositoryService.deleteDeployment(deploymentId);
				throw new Exception("部署新流程异常, 部署流程后，任务列表或者flow列表为空， deploymentId:" + deploymentId);
			}
			processTaskDao.insertBatchProcessTask(lstProcessTaskPo);
			processTaskDao.insertBatchFlow(lstFlow);
		} catch (Exception e) {
			logger.error("部署新流程异常", e);
			throw new RuntimeException("部署新流程异常,  deploymentId:" + deploymentId);
		}

	}

	private TaskDO searchNextTask(String processInstanceId,String operate) {
//		ValidationUtils.checkNotEmpty(processInstanceId, "流程实例ID为空！");

		// 查询当前流程的下一个待办任务
		List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
		if (!CollectionUtils.isEmpty(taskList) && taskList.size() > 1) {
			logger.warn("查询下一个代办任务失败！");
			throw new CommonRuntimeException("201", "查询代办任务失败!流程实例ID【" + processInstanceId + "】.");
		}
		// 流程结束-直接返回
		if (CollectionUtils.isEmpty(taskList)) {
			return null;
		}
		// 下一个代办任务处理
		Task task = taskList.get(0);
		String taskName = task.getName();
		String[] tempList = taskName.split("\\|");
		String taskStatus = tempList[2]; // 返回任务状态
		//判断是否为重新打开操作
		if(operate.equals(Constant.flowOperate.REOPEN)){
			taskStatus=Constant.flowStatus.REOPEN;
		}

		// 查询待办任务的可选事件组(流程定义ID+任务id+out出线)
		String processDefinitionId = (String) runtimeService.getVariable(processInstanceId,
				Constant.FlowValiable.PROCESS_DEFINITION_ID);
		FlowCriteria criteria = new FlowCriteria();
		criteria.setProcessId(processDefinitionId);
		criteria.setTaskKey(task.getTaskDefinitionKey());
		criteria.setType(FlowDO.FLOW_OUT);
		List<FlowDO> flowList = processTaskDao.getFlowList(criteria);
		String flowEvents = JsonTool.dataToJson(flowList);
		System.out.println(flowEvents);

		String assigneeName = ""; // 待办任务人姓名
		String assigneeId = ""; // 待办任务人id
		if(null!=task.getAssignee()){
			// 处理返回数据
			String[] userInfo = task.getAssignee().split(",");
			assigneeId = task.getAssignee(); // 待办任务人Id
			if (null != userInfo && userInfo.length == 2) { // 
				assigneeId = userInfo[0];
				assigneeName = userInfo[1];
			}
		}
		TaskDO taskDO = new TaskDO(processInstanceId, taskStatus, flowEvents, assigneeId, assigneeName,task.getId());
		return taskDO;
	}

	// 任务转交
	private void taskAssignee(FunctionFlowRequest flowRequest, User user) {
		long primaryKey = flowRequest.getPrimaryKey();
		FunctionRequireDO functionRequireDO = functionRequireDao.selectByPrimaryKey(primaryKey);
		FunctionRequireCriteria functionRequireCriteria = new FunctionRequireCriteria();
		functionRequireCriteria.setId(functionRequireDO.getId());
		if(functionRequireDO.getProcessInstanceId()==null || StringUtil.isEmpty(functionRequireDO.getProcessInstanceId())){
			startProcessFlow(primaryKey, user);
		}else{
			// 流程实例id
//		String processInstanceId = functionRequireDO.getProcessInstanceId();
			// 查询当前处理人该流程实例的任务
//		Task todoTask = taskService.createTaskQuery().processInstanceId(processInstanceId)
//				.taskAssignee(user.getUserName()).singleResult();
			String assigneeName = "";
			String taskId=functionRequireDO.getTaskId();
			if(flowRequest.getOperate().equals(Constant.flowOperate.EDIT)){
				if(functionRequireDO.getStatus().equals(Constant.flowStatus.NEWING)){
					assigneeName=functionRequireDO.getDevelopResponsibleId()+","+functionRequireDO.getDevelopResponsibleName();
					taskService.setAssignee(taskId, assigneeName);	
				}
			}else{
				// 转交任务
				assigneeName = flowRequest.getReferredUid() + "," + flowRequest.getReferredName();
				if(functionRequireDO.getStatus().equals(Constant.flowStatus.DEVELOPING)||functionRequireDO.getStatus().equals(Constant.flowStatus.NEWING)){
					functionRequireCriteria.setDevelopResponsibleId(Long.valueOf(flowRequest.getReferredUid()));
					functionRequireCriteria.setDevelopResponsibleName(flowRequest.getReferredName());
				}else if(functionRequireDO.getStatus().equals(Constant.flowStatus.TRANFER_TEST)||functionRequireDO.getStatus().equals(Constant.flowStatus.TESTING)){
					functionRequireCriteria.setTestResponsibleId(Long.valueOf(flowRequest.getReferredUid()));
					functionRequireCriteria.setTestResponsibleName(flowRequest.getReferredName());
				}
				taskService.setAssignee(taskId, assigneeName);
				// 更新数据库
				functionRequireCriteria.setCurrentHandlerId(Long.valueOf(flowRequest.getReferredUid()));
				functionRequireCriteria.setCurrentHandlerName(flowRequest.getReferredName());
				functionRequireDao.updateByPrimaryKeySelective(functionRequireCriteria);	
				//插入历史记录
				insertChangeHistory(functionRequireDO.getId().toString(),user,Constant.flowOperate.REVERSE,functionRequireDO.getBaselineVersion(),flowRequest.getOpinion());
				try {
					EmailInfo email=new EmailInfo();
					email.setStatus(functionRequireCriteria.getStatus());
					email.setApplyId(functionRequireCriteria.getId());
					email.setCurrent_user_id(String.valueOf(functionRequireCriteria.getCurrentHandlerId()));
					email.setCurrent_user_name(functionRequireCriteria.getCurrentHandlerName());
					emailService.sendBacklog(email);
				} catch (Exception e) {
					logger.error(e.toString());
				}
			}
			
		}
	}

	
	/**
	* @Title: handProcessFlow
	* @Description: 流程事件处理
	* @param @param flowRequest
	* @param @param user  
	* @return void
	* @author le
	* @throws
	*/ 
	private void handProcessFlow(FunctionFlowRequest flowRequest, User user) {
		long primaryKey = flowRequest.getPrimaryKey();
		FunctionRequireDO functionRequireDO = functionRequireDao.selectByPrimaryKey(primaryKey);
		FunctionRequireCriteria functionRequireCriteria = new FunctionRequireCriteria();
		functionRequireCriteria.setId(functionRequireDO.getId());
		String operate = flowRequest.getOperate();
		// 流程实例id
		String processInstanceId = functionRequireDO.getProcessInstanceId();
		String taskId=functionRequireDO.getTaskId();
		// 查询当前处理人该流程实例的任务
//		Task todoTask = taskService.createTaskQuery().processInstanceId(processInstanceId)
//				.taskAssignee(user.getUserName()).singleResult();

		// 数据库的流程信息转换
		@SuppressWarnings("unchecked")
		List<FlowDO> flowList = (List<FlowDO>) JsonTool.setJsonToObject(functionRequireDO.getFlowInfo(),
				new TypeReference<List<FlowDO>>() {
				}, false);
		// 从事件集合获取流程事件
		FlowDO confirmFlowDO = null;
		FlowEvent flowEvent = null;
		for (FlowDO flowDO : flowList) {
			JSONObject jsonobject=JSONObject.parseObject(flowDO.getDocumentation());
			flowEvent = PropertiesCopyUtil.parse(jsonobject, FlowEvent.class);
			if (StringUtil.isEquals(flowEvent.getCode(), operate)) {
				confirmFlowDO = flowDO;
				logger.info("选择的事件code:" + flowEvent.getCode());
				break;
			}
		}
		Map<String, Object> variables = new HashMap<String, Object>();
		// 解析条件表达式，抽取条件变量 ${key==5}
		String conditionExpression = confirmFlowDO.getConditionExpression();
		if (StringUtils.isNotBlank(conditionExpression)) {
			conditionExpression = conditionExpression.replace("{", "");
			conditionExpression = conditionExpression.replace("}", "");
			conditionExpression = conditionExpression.replace("$", "");
			String[] conditions = conditionExpression.split("==");
			if (null != conditions) {
				if (conditions.length != 2) {
					logger.warn("解析流程事件表达式失败!流程实例ID【" + processInstanceId + "】.");
					throw new CommonRuntimeException("201", "解析流程事件表达式失败!流程实例ID【" + processInstanceId + "】.");
				}
				variables = (null == variables ? new HashMap<String, Object>() : variables);
				variables.put(conditions[0], conditions[1]);
			}
			if(operate.equals(Constant.flowOperate.TRANFER_TEST)){
				FunctionRequireCriteria functionRequire=new FunctionRequireCriteria();
				functionRequire.setId(primaryKey);
				functionRequire.setActualVersion(flowRequest.getActualVersion());
				functionRequire.setActualWorkload(flowRequest.getActualWorkload());
				if(!StringUtil.isEmpty(flowRequest.getTestResponseName())){
					functionRequire.setTestResponsibleId(flowRequest.getTestResponseUid());
					functionRequire.setTestResponsibleName(flowRequest.getTestResponseName());
				}
				functionRequireDao.updateByPrimaryKeySelective(functionRequire);
				//将更新的数据赋值到functionRequireDO中
				functionRequireDO=functionRequireDao.selectByPrimaryKey(primaryKey);
			}
			// 设置下一审批人,根据业务场景设置候选人
			Object assignee = findAssignee(operate, user, functionRequireDO);
			variables.put("uid", assignee);
		}
		// 完成当前自己的待办任务
		taskService.complete(taskId, variables);
		// 搜索下一个待办任务
		TaskDO taskDO = searchNextTask(processInstanceId,operate);
		if (null != taskDO) {
			functionRequireCriteria.setProcessInstanceId(processInstanceId);
			functionRequireCriteria.setFlowInfo(taskDO.getFlowEvents());
			functionRequireCriteria.setStatus(taskDO.getTaskStatus());
			functionRequireCriteria.setTaskId(taskDO.getTaskId());
			functionRequireCriteria.setCurrentHandlerId(StringUtil.isEmpty(taskDO.getAssigneeId())?null:Long.valueOf(taskDO.getAssigneeId()));
			functionRequireCriteria.setCurrentHandlerName(taskDO.getAssigneeName());
		} else {
			functionRequireCriteria.setStatus(Constant.flowStatus.IMPLEMENTED);
			functionRequireCriteria.setProcessInstanceId("");
			functionRequireCriteria.setTaskId("");
			functionRequireCriteria.setFlowInfo("");
			functionRequireCriteria.setCurrentHandlerId(null);
			functionRequireCriteria.setCurrentHandlerName("");
		}
		// 更新数据库
		functionRequireDao.updateByPrimaryKeySelective(functionRequireCriteria);
		//插入变更历史
		insertChangeHistory(functionRequireDO.getId().toString(), user, operate,functionRequireDO.getBaselineVersion(),flowRequest.getOpinion());
		try {
			EmailInfo email=new EmailInfo();
			email.setStatus(functionRequireCriteria.getStatus());
			email.setApplyId(functionRequireCriteria.getId());
			email.setCurrent_user_id(String.valueOf(functionRequireCriteria.getCurrentHandlerId()));
			email.setCurrent_user_name(functionRequireCriteria.getCurrentHandlerName());
			emailService.sendBacklog(email);
		} catch (Exception e) {
			logger.error(e.toString());
		}
	}
	
	private Object findAssignee(String operate, User user, FunctionRequireDO functionRequireDO) {
		String assignee = "";
		if (operate.equals(Constant.flowOperate.DEVELOPING) || operate.equals(Constant.flowOperate.TESTING)) {
			assignee = user.getUserId() + "," + user.getUserName();
		} else if (operate.equals(Constant.flowOperate.TRANFER_TEST)) {
			// 查询测试人员
			if(functionRequireDO.getTestResponsibleId()==null||StringUtil.isEmpty(functionRequireDO.getTestResponsibleId().toString())){
				 throw new ServiceException("测试负责人为空,请填写测试负责人");
			}else{
				assignee = functionRequireDO.getTestResponsibleId()+","+functionRequireDO.getTestResponsibleName();
			}
		} else if (operate.equals(Constant.flowOperate.REOPEN)) {
			//查询开发负责人
			assignee = functionRequireDO.getDevelopResponsibleId() + "," + functionRequireDO.getDevelopResponsibleName();
		} else {
			// 流程完毕
			assignee = "";
		}
		return assignee;

	}
	
	private void insertChangeHistory(String functionRequirementId,User user,String operateType,String baseVersion,String remark){
		ChangeHistoryCriteria changeRecord=new ChangeHistoryCriteria();
		changeRecord.setFunctionRequireId(Long.valueOf(functionRequirementId));
		changeRecord.setCreateId(Long.valueOf(user.getUserId()));
		changeRecord.setCreateName(user.getUserName());
		changeRecord.setOperateType(operateType);
		changeRecord.setIsPermit("1");
		changeRecord.setIsDetails("0");
		changeRecord.setCreateTime(new Date());
		changeRecord.setBaselineVersion(baseVersion);
		changeRecord.setRemark(remark);
		changeHistoryDao.insert(changeRecord);
	}

}
